package org.loon.framework.android.game.core.graphics;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;

import org.loon.framework.android.game.core.LRelease;
import org.loon.framework.android.game.core.LSystem;
import org.loon.framework.android.game.core.geom.Matrix.Transform2i;
import org.loon.framework.android.game.core.graphics.device.LGraphics;
import org.loon.framework.android.game.core.graphics.opengl.GLLoader;
import org.loon.framework.android.game.core.graphics.opengl.LTexture;
import org.loon.framework.android.game.core.graphics.opengl.LTextures;
import org.loon.framework.android.game.core.graphics.opengl.LTexture.Format;
import org.loon.framework.android.game.core.resource.Resources;
import org.loon.framework.android.game.utils.CollectionUtils;
import org.loon.framework.android.game.utils.GraphicsUtils;
import org.loon.framework.android.game.utils.MathUtils;
import org.loon.framework.jni.NativeSupport;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;

/**
 * 
 * Copyright 2008 - 2012
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
/**
 * 0.3.3新增类，用以直接针对LImage进行像素操作。
 * LImage是LGame引擎特有的图像资源类，内部为Bitmap封装，其API兼顾了J2SE 及J2ME
 * 版的Java Image类(偏向于J2ME)。需要特别注意的是，由于Android环境图形资源有限，当
 * 某个LImage不再使用时，请一定要手动调用dispose()释放相关资源，以减少不必要的资源
 * 损耗。
 * @project loonframework
 * @author chenpeng
 * @email ceponline@yahoo.com.cn
 * @version 0.3.3
 */
public class LImage implements LRelease {

	/**
	 * 0.3.3新增类，用以直接针对LImage进行像素操作。
	 */
	public static class Processor implements LRelease {

		private final LImage object;

		private int[] drawPixels = null;

		private int[] processedPixels = null;

		public final int objWidth, objHeight;

		private final int pixelsSize;

		private int pixelsParam = 0;

		public Processor(int w, int h) {
			this(w, h, false);
		}

		public Processor(int w, int h, boolean alpha) {
			this(new LImage(w, h, alpha));
		}

		public Processor(LImage image) {
			this.objWidth = image.getWidth();
			this.objHeight = image.getHeight();
			this.pixelsSize = objWidth * objHeight;
			this.object = image;
			this.drawPixels = image.getPixels();
		}

		public int[] getPixels() {
			return CollectionUtils.copyOf(drawPixels);
		}

		public int getColors() {
			if (objWidth < 1 || objHeight < 1) {
				return 0;
			}
			byte[] map = new byte[(256 * 256 * 256) / 8];
			int colors = 0;
			for (int i = 0; i < objHeight; i++) {
				for (int j = 0; j < objWidth; j++) {
					int rgb = get(j, i) & 0x00ffffff;
					int index = rgb / 8;
					int bit = 1 << (rgb % 8);
					if ((map[index] & bit) == 0) {
						map[index] |= bit;
						colors++;
					}
				}
			}
			return colors;
		}

		public void drawImage(LImage img, int x, int y) {
			if (img != null) {
				LGraphics g = object.getLGraphics();
				g.drawImage(img, x, y);
				g.dispose();
				savePixels();
			}
		}

		public void drawImage(LImage img, int x, int y, int w, int h) {
			if (img != null) {
				LGraphics g = object.getLGraphics();
				g.drawImage(img, x, y, w, h);
				g.dispose();
				savePixels();
			}
		}

		public void setAlpha(final float alpha) {
			setAlphaValue((int) (255 * alpha));
		}

		public void setAlphaValue(final int alpha) {
			if (alpha < 0 || alpha > 255) {
				return;
			}
			updatePixels();
			final int decrement = 255 - alpha;
			for (int start = 0; start < pixelsSize; start += objWidth) {
				final int end = start + objWidth;
				for (int index = start; index < end; ++index) {
					final int pixel = drawPixels[index];
					int a = LColor.getAlpha(pixel);
					a -= decrement;
					if (a < 0) {
						a = 0;
					}
					processedPixels[index] = (pixel & 0x00ffffff) | a << 24;
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
		}

		public void colorFilter(LColor color) {
			this.colorFilter(color, true);
		}

		public void colorFilter(LColor color, boolean savePixels) {
			updatePixels();
			final int red = color.getRed();
			final int green = color.getGreen();
			final int blue = color.getBlue();
			final int decrementR = 255 - red;
			final int decrementG = 255 - green;
			final int decrementB = 255 - blue;
			for (int start = 0; start < pixelsSize; start += objWidth) {
				final int end = start + objWidth;
				for (int index = start; index < end; ++index) {
					final int pixel = drawPixels[index];
					int r = LColor.getRed(pixel);
					r -= decrementR;
					if (r < 0) {
						r = 0;
					}
					int g = LColor.getGreen(pixel);
					g -= decrementG;
					if (g < 0) {
						g = 0;
					}
					int b = LColor.getBlue(pixel);
					b -= decrementB;
					if (b < 0) {
						b = 0;
					}
					processedPixels[index] = (pixel & 0xff000000)
							| r << 0x00ff0000 | g << 0x0000ff00
							| b << 0x000000ff;
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
			if (savePixels) {
				drawPixels = processedPixels;
			}
		}

		public void scroll(final float dx) {
			updatePixels();
			pixelsParam += MathUtils.fromFloat(dx);
			final int dpx = MathUtils.fromInt(pixelsParam);
			if (dpx > objWidth) {
				pixelsParam -= MathUtils.toInt(objWidth);
			}
			for (int y = 0; y < objHeight; ++y) {
				final int yOffset = y * objWidth;
				for (int x = 0; x < objWidth; ++x) {
					processedPixels[x + yOffset] = drawPixels[(x + objWidth + dpx)
							% objWidth + yOffset];
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
		}

		public final void rotate(final float alpha) {
			rotate(alpha, objWidth / 2, objHeight / 2);
		}

		public final void rotate(final float alpha, final float x, final float y) {
			updatePixels();
			Transform2i transform = new Transform2i();
			transform.rotate(alpha, x, y);
			for (int j = 0; j < objHeight; ++j) {
				final int yOffset = j * objWidth;
				final int fpY = MathUtils.fromInt(j);
				final int constX = MathUtils.mul(fpY, transform.matrixs[0][1])
						+ transform.matrixs[0][2];
				final int constY = MathUtils.mul(fpY, transform.matrixs[1][1])
						+ transform.matrixs[1][2];
				for (int i = 0; i < objWidth; ++i) {
					final int fpX = MathUtils.fromInt(i);
					final int tx = MathUtils.toInt(MathUtils.mul(fpX,
							transform.matrixs[0][0]) + constX);
					if (tx < 0 || objWidth <= tx) {
						processedPixels[i + yOffset] = 0x00000000;
					} else {
						final int ty = MathUtils.toInt(MathUtils.mul(fpX,
								transform.matrixs[1][0]) + constY);
						if (ty < 0 || objHeight <= ty) {
							processedPixels[i + yOffset] = 0x00000000;
						} else {
							processedPixels[i + yOffset] = drawPixels[tx + ty
									* objWidth];
						}
					}
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
		}

		public final void zoom(final float lambda) {
			zoom(lambda, objWidth / 2, objHeight / 2);
		}

		public final void zoom(final float lambda, final float x, final float y) {
			updatePixels();
			if (0 == lambda) {
				return;
			}
			final int fpLambda = MathUtils.fromFloat(lambda);
			final int x_c = MathUtils.fromFloat(x);
			final int y_c = MathUtils.fromFloat(y);
			final int transX = x_c - MathUtils.div(x_c, fpLambda);
			final int transY = y_c - MathUtils.div(y_c, fpLambda);
			for (int j = 0; j < objHeight; ++j) {
				final int yOffset = j * objWidth;
				final int fpY = MathUtils.fromInt(j);
				final int ty = MathUtils.toInt(MathUtils.div(fpY, fpLambda)
						+ transY);
				for (int i = 0; i < objWidth; ++i) {
					if (ty < 0 || objHeight <= ty) {
						processedPixels[i + yOffset] = 0x00000000;
					} else {
						final int fpX = MathUtils.fromInt(i);
						final int tx = MathUtils.toInt(MathUtils.div(fpX,
								fpLambda) + transX);
						if (tx < 0 || objWidth <= tx) {
							processedPixels[i + yOffset] = 0x00000000;
						} else {
							processedPixels[i + yOffset] = drawPixels[tx + ty
									* objWidth];
						}
					}
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
		}

		public final void transform(final Transform2i t) {
			updatePixels();
			for (int y = 0; y < objHeight; ++y) {
				final int yOffset = y * objWidth;
				final int fpY = MathUtils.fromInt(y);
				final int constX = MathUtils.mul(fpY, t.matrixs[0][1])
						+ t.matrixs[0][2];
				final int constY = MathUtils.mul(fpY, t.matrixs[1][1])
						+ t.matrixs[1][2];
				for (int x = 0; x < objWidth; ++x) {
					final int fpX = MathUtils.fromInt(x);
					final int tx = MathUtils.toInt(MathUtils.mul(fpX,
							t.matrixs[0][0]) + constX);
					if (tx < 0 || objWidth <= tx) {
						processedPixels[x + yOffset] = 0x00000000;
					} else {
						final int ty = MathUtils.toInt(MathUtils.mul(fpX,
								t.matrixs[1][0]) + constY);
						if (ty < 0 || objHeight <= ty) {
							processedPixels[x + yOffset] = 0x00000000;
						} else {
							processedPixels[x + yOffset] = drawPixels[tx + ty
									* objWidth];
						}
					}
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
		}

		public void convolve(int[][] kernels) {
			updatePixels();
			final int M = kernels.length;
			final int N = kernels[0].length;
			for (int i = 0; i < objHeight; ++i) {
				final int iOffset = i * objWidth;
				for (int j = 0; j < objWidth; ++j) {
					int fpA = 0;
					int fpR = 0;
					int fpG = 0;
					int fpB = 0;
					for (int k = 0; k < M; ++k) {
						final int y = i - 1 + k;
						if (0 <= y && y < objHeight) {
							final int yOffset = y * objWidth;
							for (int l = 0; l < N; ++l) {
								final int x = j - 1 + l;
								if (0 <= x && x < objWidth) {
									final int color = drawPixels[x + yOffset];
									final int fp = kernels[k][l];
									fpA += MathUtils.mul(MathUtils
											.fromInt(LColor.getAlpha(color)),
											fp);
									fpR += MathUtils.mul(MathUtils
											.fromInt(LColor.getRed(color)), fp);
									fpG += MathUtils.mul(MathUtils
											.fromInt(LColor.getGreen(color)),
											fp);
									fpB += MathUtils
											.mul(MathUtils.fromInt(LColor
													.getBlue(color)), fp);
								}
							}
						}
					}
					processedPixels[j + iOffset] = LColor.getARGB(
							safeComponent(MathUtils.toInt(fpR)),
							safeComponent(MathUtils.toInt(fpG)),
							safeComponent(MathUtils.toInt(fpB)),
							safeComponent(MathUtils.toInt(fpA)));
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
			drawPixels = processedPixels;
		}

		public int safeComponent(int component) {
			return (int) MathUtils.min(MathUtils.max(0, component), 255);
		}

		public void wave(final float f, final float t) {
			updatePixels();
			final float cos = MathUtils.cos(2 * MathUtils.PI * f * t);
			final int dx = (int) (cos * 10);
			for (int y = 0; y < objHeight; ++y) {
				final int yOffset = y * objWidth;
				for (int x = 0; x < objWidth; ++x) {
					if (y % 2 == 0) {
						processedPixels[x + yOffset] = drawPixels[(x + objWidth + dx)
								% objWidth + yOffset];
					} else {
						processedPixels[x + yOffset] = drawPixels[(x + objWidth - dx)
								% objWidth + yOffset];
					}
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
		}

		public final void mul(LColor mulColor) {
			mul(mulColor.getAlpha(), mulColor.getRed(), mulColor.getGreen(),
					mulColor.getBlue());
		}

		public final void mul(int red, int green, int blue) {
			mul(255, red, green, blue);
		}

		public final void mul(int alpha, int red, int green, int blue) {
			updatePixels();
			final float a = alpha / 255f;
			final float r = red / 255f;
			final float g = green / 255f;
			final float b = blue / 255f;
			for (int j = 0; j < objHeight; ++j) {
				final int yOffset = j * objWidth;
				final int end = yOffset + objWidth;
				for (int i = yOffset; i < end; ++i) {
					final int pixel = drawPixels[i];
					processedPixels[i] = LColor.getARGB(
							(int) (LColor.getRed(pixel) * r),
							(int) (LColor.getGreen(pixel) * g),
							(int) (LColor.getBlue(pixel) * b),
							(int) (LColor.getAlpha(pixel) * a));
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
			drawPixels = processedPixels;
		}

		public final void invert() {
			updatePixels();
			for (int j = 0; j < objHeight; ++j) {
				final int yOffset = j * objWidth;
				final int end = yOffset + objWidth;
				for (int i = yOffset; i < end; ++i) {
					final int color = drawPixels[i];
					processedPixels[i] = LColor
							.getARGB(255 - LColor.getRed(color),
									255 - LColor.getGreen(color),
									255 - LColor.getBlue(color),
									LColor.getAlpha(color));
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
			drawPixels = processedPixels;
		}

		protected final void set(int x, int y, int pixel) {
			drawPixels[x + y * objWidth] = pixel;
		}

		protected final int get(int x, int y) {
			return drawPixels[x + y * objWidth];
		}

		public final void transparency() {
			this.updatePixels();
			int transparentColor = get(1, 0);
			if (0x00000000 != transparentColor) {
				for (int x = 0; x < objWidth; ++x) {
					for (int y = 0; y < objHeight; ++y) {
						if (get(x, y) == transparentColor) {
							set(x, y, 0x00000000);
						}
					}
				}
			}
		}

		public static final int[][] blurKernel() {
			final float centerValue = 0.2f;
			final float edgeValue = (1 - centerValue) / 8;
			final int c = MathUtils.fromFloat(centerValue);
			final int e = MathUtils.fromFloat(edgeValue);
			int[][] kernels = { { e, e, e }, { e, c, e }, { e, e, e } };
			return kernels;
		}

		public static final int[][] gaussianBlurKernel() {
			final float sum = 16;
			final float centerValue = 4f / sum;
			final float edgeValue = 1f / sum;
			final float midValues = 2f / sum;
			final int c = MathUtils.fromFloat(centerValue);
			final int e = MathUtils.fromFloat(edgeValue);
			final int m = MathUtils.fromFloat(midValues);
			int[][] kernels = { { e, m, e }, { m, c, m }, { e, m, e } };
			return kernels;
		}

		public static final int[][] edgeKernel() {
			final float centerValue = 1;
			final float edgeValue = -1 / 8;
			final int c = MathUtils.fromFloat(centerValue);
			final int e = MathUtils.fromFloat(edgeValue);
			int[][] kernels = { { e, e, e }, { e, c, e }, { e, e, e } };
			return kernels;
		}

		public static final int[][] embossKernel() {
			final float centerValue = 0.5f;
			final float edgeValue = 1 - centerValue;
			final int c = MathUtils.fromFloat(centerValue);
			final int e = MathUtils.fromFloat(edgeValue);
			int[][] kernels = { { 0, 0, 0 }, { 0, c, 0 }, { 0, 0, e } };
			return kernels;
		}

		public static final int[][] sharpenKernel() {
			final float centerValue = 5;
			final float edgeValue = -1;
			final int c = MathUtils.fromFloat(centerValue);
			final int e = MathUtils.fromFloat(edgeValue);
			int[][] kernels = { { 0, e, 0 }, { e, c, e }, { 0, e, 0 } };
			return kernels;
		}

		private final int colorLerp(final int fpX, final int color1,
				final int color2) {
			final int fpY = MathUtils.ONE_FIXED - fpX;
			final int a = MathUtils.toInt(MathUtils.mul(
					MathUtils.fromInt(LColor.getAlpha(color1)), fpY)
					+ MathUtils.mul(MathUtils.fromInt(LColor.getAlpha(color2)),
							fpX));
			final int r = MathUtils.toInt(MathUtils.mul(
					MathUtils.fromInt(LColor.getRed(color1)), fpY)
					+ MathUtils.mul(MathUtils.fromInt(LColor.getRed(color2)),
							fpX));
			final int g = MathUtils.toInt(MathUtils.mul(
					MathUtils.fromInt(LColor.getGreen(color1)), fpY)
					+ MathUtils.mul(MathUtils.fromInt(LColor.getGreen(color2)),
							fpX));
			final int b = MathUtils.toInt(MathUtils.mul(
					MathUtils.fromInt(LColor.getBlue(color1)), fpY)
					+ MathUtils.mul(MathUtils.fromInt(LColor.getBlue(color2)),
							fpX));
			return LColor.getARGB(r, g, b, a);
		}

		public final void fourCornersGradient(final int topLeftColor,
				final int topRightColor, final int bottomRightColor,
				final int bottomLeftColor) {
			updatePixels();
			final int fpH = MathUtils.fromInt(objHeight);
			final int fpW = MathUtils.fromInt(objWidth);
			for (int y = 0; y < objHeight; ++y) {
				final int yOffset = y * objWidth;
				final int fpYRatio = MathUtils.div(MathUtils.fromInt(y), fpH);
				final int leftColor = colorLerp(fpYRatio, topLeftColor,
						bottomLeftColor);
				final int rightColor = colorLerp(fpYRatio, topRightColor,
						bottomRightColor);
				for (int x = 0; x < objWidth; ++x) {
					final int fpXRatio = MathUtils.div(MathUtils.fromInt(x),
							fpW);
					processedPixels[x + yOffset] = colorLerp(fpXRatio,
							leftColor, rightColor);
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
			drawPixels = processedPixels;
		}

		public void interleave(Processor imgProcessor, final int[][] mask) {
			final int m = mask.length, n = mask[0].length;
			int i = 0, j = 0;
			for (int y = 0; y < objHeight; ++y) {
				final int yOffset = y * objWidth;
				for (int x = 0; x < objWidth; ++x) {
					final int offset = x + yOffset;
					processedPixels[offset] = (mask[i][j] == 1) ? drawPixels[offset]
							: imgProcessor.drawPixels[offset];
					if (++j >= n) {
						j = 0;
					}
				}
				if (++i >= m) {
					i = 0;
				}
			}
			object.setPixels(processedPixels, objWidth, objHeight);
			drawPixels = processedPixels;
		}

		public void applyGradient() {
			updatePixels();
			final int transparentColor = get(1, 0);
			for (int y = 0; y < objHeight; ++y) {
				final int yOffset = y * objWidth;
				final int mulColor = get(0, y);
				if (mulColor != transparentColor) {
					final float a = LColor.getAlpha(mulColor) / 255f;
					final float r = LColor.getRed(mulColor) / 255f;
					final float g = LColor.getGreen(mulColor) / 255f;
					final float b = LColor.getBlue(mulColor) / 255f;
					final int end = yOffset + objWidth;
					for (int j = yOffset; j < end; ++j) {
						final int pixel = drawPixels[j];
						if (pixel != transparentColor)
							drawPixels[j] = LColor.getARGB((int) MathUtils.min(
									255, (LColor.getRed(pixel) * r)),
									(int) MathUtils.min(255,
											(LColor.getGreen(pixel) * g)),
									(int) MathUtils.min(
											(LColor.getBlue(pixel) * b), 255),
									(int) MathUtils.min(255,
											(LColor.getAlpha(pixel) * a)));
					}
				}
			}
			object.setPixels(drawPixels, objWidth, objHeight);
		}

		public void savePixels() {
			this.drawPixels = object.getPixels();
		}

		private final void updatePixels() {
			if (drawPixels == null) {
				savePixels();
			}
			if (processedPixels == null || processedPixels == drawPixels) {
				processedPixels = new int[pixelsSize];
			}
		}

		public LImage getImage() {
			return object;
		}

		public LTexture getTexture() {
			if (object != null) {
				return object.getTexture();
			}
			return null;
		}

		public int getWidth() {
			return objWidth;
		}

		public int getHeight() {
			return objHeight;
		}

		public void dispose() {
			if (object != null) {
				object.dispose();
			}
			this.drawPixels = null;
			this.processedPixels = null;
		}

	}

	/**
	 * 0.3.3版新增类，用以处理TGA格式图像（仅支持24&32位图）
	 */
	public static class LFormatTGA {

		private static final int TGA_HEADER_SIZE = 18;

		private static final int TGA_HEADER_INVALID = 0;

		private static final int TGA_HEADER_UNCOMPRESSED = 1;

		private static final int TGA_HEADER_COMPRESSED = 2;

		public static class State implements LRelease {

			public int type;

			public int pixelDepth;

			public int width;

			public int height;

			public int[] pixels;

			public void dispose() {
				if (pixels != null) {
					pixels = null;
				}
			}

		}

		public static State inJustDecode(String res) throws IOException {
			return inJustDecode(Resources.openResource(res));
		}

		public static State inJustDecode(InputStream in) throws IOException {
			return loadHeader(in, new State());
		}

		private static State loadHeader(InputStream in, State info)
				throws IOException {

			in.read();
			in.read();

			info.type = (byte) in.read();

			in.read();
			in.read();
			in.read();
			in.read();
			in.read();
			in.read();
			in.read();
			in.read();
			in.read();

			info.width = (in.read() & 0xff) | ((in.read() & 0xff) << 8);
			info.height = (in.read() & 0xff) | ((in.read() & 0xff) << 8);

			info.pixelDepth = in.read() & 0xff;

			return info;
		}

		private static final short getUnsignedByte(byte[] bytes, int byteIndex) {
			return (short) (bytes[byteIndex] & 0xFF);
		}

		private static final int getUnsignedShort(byte[] bytes, int byteIndex) {
			return (getUnsignedByte(bytes, byteIndex + 1) << 8)
					+ getUnsignedByte(bytes, byteIndex + 0);
		}

		private static void readBuffer(InputStream in, byte[] buffer)
				throws IOException {
			int bytesRead = 0;
			int bytesToRead = buffer.length;
			for (; bytesToRead > 0;) {
				int read = in.read(buffer, bytesRead, bytesToRead);
				bytesRead += read;
				bytesToRead -= read;
			}
		}

		private static final void skipBytes(InputStream in, long toSkip)
				throws IOException {
			for (; toSkip > 0L;) {
				long skipped = in.skip(toSkip);
				if (skipped > 0) {
					toSkip -= skipped;
				} else if (skipped < 0) {
					toSkip = 0;
				}
			}
		}

		private static final int compareFormatHeader(InputStream in,
				byte[] header) throws IOException {

			readBuffer(in, header);
			boolean hasPalette = false;
			int result = TGA_HEADER_INVALID;

			int imgIDSize = getUnsignedByte(header, 0);

			if ((header[1] != (byte) 0) && (header[1] != (byte) 1)) {
				return TGA_HEADER_INVALID;
			}

			switch (getUnsignedByte(header, 2)) {
			case 0:
				result = TGA_HEADER_UNCOMPRESSED;
				break;
			case 1:
				hasPalette = true;
				result = TGA_HEADER_UNCOMPRESSED;
				throw new RuntimeException(
						"Indexed State is not yet supported !");
			case 2:
				result = TGA_HEADER_UNCOMPRESSED;
				break;
			case 3:
				result = TGA_HEADER_UNCOMPRESSED;
				break;
			case 9:
				hasPalette = true;
				result = TGA_HEADER_COMPRESSED;
				throw new RuntimeException(
						"Indexed State is not yet supported !");
			case 10:
				result = TGA_HEADER_COMPRESSED;
				break;
			case 11:
				result = TGA_HEADER_COMPRESSED;
				break;
			default:
				return TGA_HEADER_INVALID;
			}
			if (!hasPalette) {
				if (getUnsignedShort(header, 3) != 0) {
					return TGA_HEADER_INVALID;
				}
			}
			if (!hasPalette) {
				if (getUnsignedShort(header, 5) != 0) {
					return TGA_HEADER_INVALID;
				}
			}

			short paletteEntrySize = getUnsignedByte(header, 7);
			if (!hasPalette) {
				if (paletteEntrySize != 0) {
					return TGA_HEADER_INVALID;
				}
			} else {
				if ((paletteEntrySize != 15) && (paletteEntrySize != 16)
						&& (paletteEntrySize != 24) && (paletteEntrySize != 32)) {
					return TGA_HEADER_INVALID;
				}
			}

			if (getUnsignedShort(header, 8) != 0) {
				return TGA_HEADER_INVALID;
			}

			if (getUnsignedShort(header, 10) != 0) {
				return TGA_HEADER_INVALID;
			}

			switch (getUnsignedByte(header, 16)) {
			case 1:
			case 8:
			case 15:
			case 16:
				throw new RuntimeException(
						"this State with non RGB or RGBA pixels are not yet supported.");
			case 24:
			case 32:
				break;
			default:
				return TGA_HEADER_INVALID;
			}

			if (imgIDSize != 0) {
				skipBytes(in, imgIDSize);
			}

			return result;
		}

		private static final void writePixel(int[] pixels, final byte red,
				final byte green, final byte blue, final byte alpha,
				final boolean hasAlpha, final int offset) {
			int pixel;
			if (hasAlpha) {
				pixel = (red & 0xff);
				pixel |= ((green & 0xff) << 8);
				pixel |= ((blue & 0xff) << 16);
				pixel |= ((alpha & 0xff) << 24);
				pixels[offset / 4] = pixel;
			} else {
				pixel = (red & 0xff);
				pixel |= ((green & 0xff) << 8);
				pixel |= ((blue & 0xff) << 16);
				pixels[offset / 4] = pixel;
			}
		}

		private static int[] readBuffer(InputStream in, int width, int height,
				int srcBytesPerPixel, boolean acceptAlpha,
				boolean flipVertically) throws IOException {

			int[] pixels = new int[width * height];
			byte[] buffer = new byte[srcBytesPerPixel];

			final boolean copyAlpha = (srcBytesPerPixel == 4) && acceptAlpha;
			final int dstBytesPerPixel = acceptAlpha ? srcBytesPerPixel : 3;
			final int trgLineSize = width * dstBytesPerPixel;

			int dstByteOffset = 0;

			for (int y = 0; y < height; y++) {
				for (int x = 0; x < width; x++) {
					int read = in.read(buffer, 0, srcBytesPerPixel);

					if (read < srcBytesPerPixel) {
						return pixels;
					}
					int actualByteOffset = dstByteOffset;
					if (!flipVertically) {
						actualByteOffset = ((height - y - 1) * trgLineSize)
								+ (x * dstBytesPerPixel);
					}

					if (copyAlpha) {
						writePixel(pixels, buffer[2], buffer[1], buffer[0],
								buffer[3], true, actualByteOffset);
					} else {
						writePixel(pixels, buffer[2], buffer[1], buffer[0],
								(byte) 0, false, actualByteOffset);
					}

					dstByteOffset += dstBytesPerPixel;
				}
			}
			return pixels;
		}

		private static void loadUncompressed(byte[] header, State tga,
				InputStream in, boolean acceptAlpha, boolean flipVertically)
				throws IOException {

			// 图像宽
			int orgWidth = getUnsignedShort(header, 12);

			// 图像高
			int orgHeight = getUnsignedShort(header, 14);

			// 图像位图(24&32)
			int pixelDepth = getUnsignedByte(header, 16);

			tga.width = orgWidth;
			tga.height = orgHeight;
			tga.pixelDepth = pixelDepth;

			boolean isOriginBottom = (header[17] & 0x20) == 0;

			if (!isOriginBottom) {
				flipVertically = !flipVertically;
			}

			// 不支持的格式
			if ((orgWidth <= 0) || (orgHeight <= 0)
					|| ((pixelDepth != 24) && (pixelDepth != 32))) {
				throw new IOException("Invalid texture information !");
			}

			int bytesPerPixel = (pixelDepth / 8);

			// 获取图像数据并转为int[]
			tga.pixels = readBuffer(in, orgWidth, orgHeight, bytesPerPixel,
					acceptAlpha, flipVertically);
			// 图像色彩模式
			tga.type = (acceptAlpha && (bytesPerPixel == 4) ? 4 : 3);
		}

		private static void loadCompressed(byte[] header, State tga,
				InputStream in, boolean acceptAlpha, boolean flipVertically)
				throws IOException {

			int orgWidth = getUnsignedShort(header, 12);
			int orgHeight = getUnsignedShort(header, 14);
			int pixelDepth = getUnsignedByte(header, 16);

			tga.width = orgWidth;
			tga.height = orgHeight;
			tga.pixelDepth = pixelDepth;

			boolean isOriginBottom = (header[17] & 0x20) == 0;

			if (!isOriginBottom) {
				flipVertically = !flipVertically;
			}

			if ((orgWidth <= 0) || (orgHeight <= 0)
					|| ((pixelDepth != 24) && (pixelDepth != 32))) {
				throw new IOException("Invalid texture information !");
			}

			int bytesPerPixel = (pixelDepth / 8);
			int pixelCount = orgHeight * orgWidth;
			int currentPixel = 0;

			byte[] colorBuffer = new byte[bytesPerPixel];

			int width = orgWidth;
			int height = orgHeight;

			final int dstBytesPerPixel = (acceptAlpha && (bytesPerPixel == 4) ? 4
					: 3);
			final int trgLineSize = orgWidth * dstBytesPerPixel;

			int[] pixels = new int[width * height];

			int dstByteOffset = 0;

			do {
				int chunkHeader = 0;
				try {
					chunkHeader = (byte) in.read() & 0xFF;
				} catch (IOException e) {
					throw new IOException(
							"Could not read RLE imageData header !");
				}

				boolean repeatColor;

				if (chunkHeader < 128) {
					chunkHeader++;
					repeatColor = false;
				} else {
					chunkHeader -= 127;
					readBuffer(in, colorBuffer);
					repeatColor = true;
				}

				for (int counter = 0; counter < chunkHeader; counter++) {
					if (!repeatColor) {
						readBuffer(in, colorBuffer);
					}

					int x = currentPixel % orgWidth;
					int y = currentPixel / orgWidth;

					int actualByteOffset = dstByteOffset;
					if (!flipVertically) {
						actualByteOffset = ((height - y - 1) * trgLineSize)
								+ (x * dstBytesPerPixel);
					}

					if (dstBytesPerPixel == 4) {
						writePixel(pixels, colorBuffer[2], colorBuffer[1],
								colorBuffer[0], colorBuffer[3], true,
								actualByteOffset);
					} else {
						writePixel(pixels, colorBuffer[2], colorBuffer[1],
								colorBuffer[0], (byte) 0, false,
								actualByteOffset);
					}

					dstByteOffset += dstBytesPerPixel;

					currentPixel++;

					if (currentPixel > pixelCount) {
						throw new IOException("Too many pixels read !");
					}
				}
			} while (currentPixel < pixelCount);

			tga.pixels = pixels;
			tga.type = dstBytesPerPixel;

		}

		public static State load(String res) throws IOException {
			return load(res, new State());
		}

		public static State load(String res, State tag) throws IOException {
			InputStream in = Resources.openResource(res);
			State tga = load(in, tag, true, false);
			if (in != null) {
				try {
					in.close();
					in = null;
				} catch (Exception e) {
				}
			}
			return tga;
		}

		public static State load(InputStream in, State tga,
				boolean acceptAlpha, boolean flipVertically) throws IOException {
			if (in.available() < TGA_HEADER_SIZE) {
				return (null);
			}
			byte[] header = new byte[TGA_HEADER_SIZE];
			final int headerType = compareFormatHeader(in, header);
			if (headerType == TGA_HEADER_INVALID) {
				return (null);
			}
			if (headerType == TGA_HEADER_UNCOMPRESSED) {
				loadUncompressed(header, tga, in, acceptAlpha, flipVertically);
			} else if (headerType == TGA_HEADER_COMPRESSED) {
				loadCompressed(header, tga, in, acceptAlpha, flipVertically);
			} else {
				throw new IOException("State file be type 2 or type 10 !");
			}
			return tga;
		}
	}

	private final static String tgaExtension = ".tga";

	// 自0.3.2版起新增的YUV处理类（构造同Android自带的YuvImage基本一致）
	public static class YuvImage {

		private static final int CSHIFT = 16;

		private static final int CYR = 19595;

		private static final int CYG = 38470;

		private static final int CYB = 7471;

		private static final int CUR = -11059;

		private static final int CUG = -21709;

		private static final int CUB = 32768;

		private static final int CVR = 32768;

		private static final int CVG = -27439;

		private static final int CVB = -5329;

		public static final int NV21 = 0x11;

		public static final int YUY2 = 0x14;

		public static int getBitsPerPixel(int format) {
			switch (format) {
			case NV21:
				return 12;
			case YUY2:
				return 16;
			}
			return 1;
		}

		private int format;

		private byte[] data;

		private int[] strides;

		private int width;

		private int height;

		public YuvImage(int width, int height, int format) {
			byte[] yuv = new byte[width * height * getBitsPerPixel(format)];
			set(yuv, format, width, height, null);
		}

		public YuvImage(byte[] yuv, int format, int width, int height,
				int[] strides) {
			set(yuv, format, width, height, strides);
		}

		public void set(byte[] yuv, int format, int width, int height, int[] s) {
			if (format != NV21 && format != YUY2) {
				throw new IllegalArgumentException("only support NV21 "
						+ "and YUY2 !");
			}

			if (width <= 0 || height <= 0) {
				throw new IllegalArgumentException(
						"width and height must large than 0 !");
			}

			if (yuv == null) {
				throw new IllegalArgumentException("yuv cannot be null !");
			}

			if (strides == null) {
				strides = calculateStrides(width, format);
			} else {
				strides = s;
			}
			this.data = yuv;
			this.format = format;
			this.width = width;
			this.height = height;
		}

		public int[] calculateOffsets(int left, int top) {
			int[] offsets = null;
			if (format == NV21) {
				offsets = new int[] {
						top * strides[0] + left,
						height * strides[0] + top / 2 * strides[1] + left / 2
								* 2 };
				return offsets;
			}

			if (format == YUY2) {
				offsets = new int[] { top * strides[0] + left / 2 * 4 };
				return offsets;
			}

			return offsets;
		}

		public static YuvImage toYuvImage(LImage img, int format) {
			return toYuvImage(img, format);
		}

		public static YuvImage toYuvImage(LImage img, int format, int paddings) {
			int width = img.getWidth();
			int height = img.getHeight();
			int stride = width + paddings;
			int[] pixels = new int[stride * height];
			img.getPixels(pixels, 0, stride, 0, 0, width, height);
			byte[] yuv = convertPixelsToYuvs(pixels, stride, height, format);
			int[] strides = null;
			if (format == NV21) {
				strides = new int[] { stride, stride };
			} else if (format == YUY2) {
				strides = new int[] { stride * 2 };
			}
			return new YuvImage(yuv, format, width, height, strides);
		}

		private static int[] calculateStrides(int width, int format) {
			int[] strides = null;
			if (format == NV21) {
				strides = new int[] { width, width };
				return strides;
			}

			if (format == YUY2) {
				strides = new int[] { width * 2 };
				return strides;
			}

			return strides;
		}

		private static void argb2yuv(int argb, byte[] yuv) {
			int[] rgbs = LColor.getRGBs(argb);
			int r = rgbs[0];
			int g = rgbs[1];
			int b = rgbs[2];
			yuv[0] = (byte) ((CYR * r + CYG * g + CYB * b) >> CSHIFT);
			yuv[1] = (byte) (((CUR * r + CUG * g + CUB * b) >> CSHIFT) + 128);
			yuv[2] = (byte) (((CVR * r + CVG * g + CVB * b) >> CSHIFT) + 128);
		}

		private static byte[] convertPixelsToYuvs(int[] argb, int width,
				int height, int format) {
			byte[] yuv = new byte[width * height
					* YuvImage.getBitsPerPixel(format)];
			if (format == YuvImage.NV21) {
				int vuStart = width * height;
				byte[] yuvColor = new byte[3];
				for (int row = 0; row < height; ++row) {
					for (int col = 0; col < width; ++col) {
						int idx = row * width + col;
						argb2yuv(argb[idx], yuvColor);
						yuv[idx] = yuvColor[0];
						if ((row & 1) == 0 && (col & 1) == 0) {
							int offset = row / 2 * width + col / 2 * 2;
							yuv[vuStart + offset] = yuvColor[2];
							yuv[vuStart + offset + 1] = yuvColor[1];
						}
					}
				}
			} else if (format == YuvImage.YUY2) {
				byte[] yuvColor0 = new byte[3];
				byte[] yuvColor1 = new byte[3];
				for (int row = 0; row < height; ++row) {
					for (int col = 0; col < width; col += 2) {
						int idx = row * width + col;
						argb2yuv(argb[idx], yuvColor0);
						argb2yuv(argb[idx + 1], yuvColor1);
						int offset = idx / 2 * 4;
						yuv[offset] = yuvColor0[0];
						yuv[offset + 1] = yuvColor0[1];
						yuv[offset + 2] = yuvColor1[0];
						yuv[offset + 3] = yuvColor0[2];
					}
				}
			}
			return yuv;
		}

		public ByteBuffer getByteBuffer() {
			return NativeSupport.getByteBuffer(data);
		}

		public byte[] getYuvData() {
			return data;
		}

		public int getYuvFormat() {
			return format;
		}

		public int[] getStrides() {
			return strides;
		}

		public int getWidth() {
			return width;
		}

		public int getHeight() {
			return height;
		}

	}

	private final static ArrayList<LImage> images = new ArrayList<LImage>(100);

	private HashMap<Integer, LImage> subs;

	private Bitmap bitmap;

	private String fileName;

	private LGraphics g;

	private int width, height;

	private boolean isClose, isUpdate, isAutoDispose = true;

	private LTexture texture;

	private Format format = Format.DEFAULT;

	/**
	 * 从指定输入流转化出LImage图形，并尝试根据transparency将其
	 * 设定为RGB565（False时）或ARGB4444（True时）格式。当然，
	 * 由于transparency传递给Android后，只是修改了加载时的首选配
	 * 置，而没有强制改变文件格式，所以图片并非一定会以RGB565
	 * 或ARGB4444格式进行显示，图片本身的色彩模式是主要决定因素。
	 * @param in
	 * @param transparency
	 * @return
	 */
	public static LImage createImage(InputStream in, boolean transparency) {
		return GraphicsUtils.loadImage(in, transparency);
	}

	/**
	 * 将指定的比特数组转换为LImage图形。
	 * @param buffer
	 * @return
	 */
	public static LImage createImage(byte[] buffer) {
		return GraphicsUtils.loadImage(buffer, true);
	}

	/**
	 * 将指定的比特数组转换为LImage图形，并尝试根据transparency
	 * 将其设定为RGB565（False时）或ARGB4444（True 时）格式。
	 * 当然，由于transparency传递给Android后，只是修改了加载时的
	 * 首选配置，而没有强制改变文件格式，所以图片并非一定会以
	 * RGB565或ARGB4444格式进行显示，图片本身的色彩模式是主
	 * 要决定因素。
	 * @param buffer
	 * @param transparency
	 * @return
	 */
	public static LImage createImage(byte[] buffer, boolean transparency) {
		return GraphicsUtils.loadImage(buffer, transparency);
	}

	/**
	 * 创建一张指定大小的LImage图像，并根据transparency将其设定
	 * 为RGB565（False时）或ARGB4444（True时）格式。(此设定
	 * 将绝对生成指定的图像格式)
	 * @param width
	 * @param height
	 * @param transparency
	 * @return
	 */
	public static LImage createImage(int width, int height, boolean transparency) {
		return new LImage(width, height, transparency);
	}

	/**
	 * 创建一张指定大小的LImage图像。
	 * @param width
	 * @param height
	 * @return
	 */
	public static LImage createImage(int width, int height) {
		return new LImage(width, height, false);
	}

	/**
	 * 创建一张指定大小的LImage图像，并且将其设定为Config所指
	 * 定的图像格式。
	 * @param width
	 * @param height
	 * @param config
	 * @return
	 */
	public static LImage createImage(int width, int height, Config config) {
		return new LImage(width, height, config);
	}

	/**
	 * 从指定比特数组中，截取指定长度数据生成LImage 图像，并尝
	 * 试根据transparency将其设定为RGB565（False时）或ARGB4444
	 * （True时）格式。当然，由于transparency传递给Android后，
	 * 只是修改了加载时的首选配置，而没有强制改变文件格式，所以
	 * 图片并非一定会以RGB565 或ARGB4444格式进行显示，图片本
	 * 身的色彩模式是主要决定因素。
	 * @param imageData
	 * @param imageOffset
	 * @param imageLength
	 * @param transparency
	 * @return
	 */
	public static LImage createImage(byte[] imageData, int imageOffset,
			int imageLength, boolean transparency) {
		return GraphicsUtils.loadImage(imageData, imageOffset, imageLength,
				transparency);
	}

	/**
	 * 从指定比特数组中，截取指定长度数据生成LImage 图像。
	 * @param imageData
	 * @param imageOffset
	 * @param imageLength
	 * @return
	 */
	public static LImage createImage(byte[] imageData, int imageOffset,
			int imageLength) {
		return GraphicsUtils.loadImage(imageData, imageOffset, imageLength,
				false);
	}

	/**
	 * 加载APK 中指定路径资源为LImage。（有缓存）
	 * @param fileName
	 * @return
	 */
	public static LImage createImage(String fileName) {
		return GraphicsUtils.loadImage(fileName);
	}

	/**
	 * 以指定像素集合生成LImage文件
	 * 根据指定像素集合，创建一张指定大小的LImage图像，当
	 * processAlpha为True时，图像格式为ARGB4444，否则为RGB565。
	 * @param rgb
	 * @param width
	 * @param height
	 * @param processAlpha
	 * @return
	 */
	public static final LImage createRGBImage(int[] rgb, int width, int height,
			boolean processAlpha) {
		Bitmap bitmap = null;
		try {
			Bitmap.Config config;
			if (processAlpha) {
				config = Bitmap.Config.ARGB_8888;
			} else {
				config = Bitmap.Config.RGB_565;
			}
			bitmap = Bitmap.createBitmap(rgb, width, height, config);
		} catch (Exception e) {
			LSystem.gc();
			Bitmap.Config config;
			if (processAlpha) {
				config = Bitmap.Config.ARGB_8888;
			} else {
				config = Bitmap.Config.RGB_565;
			}
			bitmap = Bitmap.createBitmap(rgb, width, height, config);
		}
		return new LImage(bitmap);
	}

	/**
	 * 生成旋转为指定角度的图像
	 * 从指定的LImage中指定坐标起始，截取指定宽度及高度的图片，
	 * 并且根据transform参数旋转为之指定角度（相关int 参数来自于
	 * Trans 中静态常量），并返回新生的LImage图像。
	 * @param image
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 * @param transform
	 * @return
	 */
	public static LImage createImage(LImage image, int x, int y, int width,
			int height, int transform) {
		int[] buf = new int[width * height];
		image.getPixels(buf, 0, width, x, y, width, height);
		int th;
		int tw;
		if ((transform & 4) != 0) {
			th = width;
			tw = height;
		} else {
			th = height;
			tw = width;
		}
		if (transform != 0) {
			int[] trans = new int[buf.length];
			int sp = 0;
			for (int sy = 0; sy < height; sy++) {
				int tx;
				int ty;
				int td;

				switch (transform) {
				case LGraphics.TRANS_ROT90:
					tx = tw - sy - 1;
					ty = 0;
					td = tw;
					break;
				case LGraphics.TRANS_ROT180:
					tx = tw - 1;
					ty = th - sy - 1;
					td = -1;
					break;
				case LGraphics.TRANS_ROT270:
					tx = sy;
					ty = th - 1;
					td = -tw;
					break;
				case LGraphics.TRANS_MIRROR:
					tx = tw - 1;
					ty = sy;
					td = -1;
					break;
				case LGraphics.TRANS_MIRROR_ROT90:
					tx = tw - sy - 1;
					ty = th - 1;
					td = -tw;
					break;
				case LGraphics.TRANS_MIRROR_ROT180:
					tx = 0;
					ty = th - sy - 1;
					td = 1;
					break;
				case LGraphics.TRANS_MIRROR_ROT270:
					tx = sy;
					ty = 0;
					td = tw;
					break;
				default:
					throw new RuntimeException("illegal transformation: "
							+ transform);
				}

				int tp = ty * tw + tx;
				for (int sx = 0; sx < width; sx++) {
					trans[tp] = buf[sp++];
					tp += td;
				}
			}
			buf = trans;
		}

		return createRGBImage(buf, tw, th, true);
	}

	/**
	 * 创建出指定数量、指定大小的LImage图像，并根据transparency
	 * 将其设定为RGB565（False时）或ARGB4444（True 时）格式。
	 * 
	 * @param count
	 * @param w
	 * @param h
	 * @param transparency
	 * @return
	 */
	public static LImage[] createImage(int count, int w, int h,
			boolean transparency) {
		LImage[] image = new LImage[count];
		for (int i = 0; i < image.length; i++) {
			image[i] = new LImage(w, h, transparency);
		}
		return image;
	}

	/**
	 * 创建出指定数量、指定大小的LImage图像，并根据Config设置
	 * 将其生成为指定格式。
	 * 
	 * @param count
	 * @param w
	 * @param h
	 * @param config
	 * @return
	 */
	public static LImage[] createImage(int count, int w, int h, Config config) {
		LImage[] image = new LImage[count];
		for (int i = 0; i < image.length; i++) {
			image[i] = new LImage(w, h, config);
		}
		return image;
	}

	public LImage(String fileName) {
		this(fileName, null);
	}

	public LImage(String fileName, Config config) {
		if (fileName == null) {
			throw new RuntimeException("file name is null !");
		}
		String res;
		if (fileName.startsWith("/")) {
			res = fileName.substring(1);
		} else {
			res = fileName;
		}
		this.fileName = fileName;
		Bitmap bitmap = null;
		if (fileName.toLowerCase().lastIndexOf(tgaExtension) != -1) {
			try {
				LFormatTGA.State tga = LFormatTGA.load(res);
				if (tga != null) {
					bitmap = Bitmap.createBitmap(tga.pixels, tga.width,
							tga.height, tga.type == 4 ? Bitmap.Config.ARGB_8888
									: Bitmap.Config.RGB_565);
					tga.dispose();
					tga = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			bitmap = GraphicsUtils.loadBitmap(res, config);
		}
		if (bitmap == null) {
			throw new RuntimeException("File " + fileName + " was not found !");
		}
		setBitmap(bitmap);
		if (!images.contains(this)) {
			images.add(this);
		}
	}

	public LImage(int width, int height) {
		this(width, height, false);
	}

	/**
	 * 构建一个LImage(true:ARGB8888或false:RGB565)
	 * 
	 * @param width
	 * @param height
	 * @param transparency
	 */
	public LImage(int width, int height, boolean transparency) {
		try {
			LSystem.gc(100, 1);
			this.width = width;
			this.height = height;
			if (transparency) {
				this.bitmap = Bitmap.createBitmap(width, height,
						Bitmap.Config.ARGB_8888);
			} else {
				this.bitmap = Bitmap.createBitmap(width, height,
						Bitmap.Config.RGB_565);
			}
		} catch (Exception e) {
			try {
				GraphicsUtils.destroy();
				LTextures.destroyAll();
				LSystem.gc();
				this.width = width;
				this.height = height;
				if (transparency) {
					this.bitmap = Bitmap.createBitmap(width, height,
							Bitmap.Config.ARGB_8888);
				} else {
					this.bitmap = Bitmap.createBitmap(width, height,
							Bitmap.Config.RGB_565);
				}
			} catch (Exception ex) {
				LSystem.gc();
			}
		}
		if (!images.contains(this)) {
			images.add(this);
		}
	}

	public LImage(int width, int height, Config config) {
		this.width = width;
		this.height = height;
		this.bitmap = Bitmap.createBitmap(width, height, config);
		if (!images.contains(this)) {
			images.add(this);
		}
	}

	public LImage(LImage img) {
		this(img.getBitmap());
	}

	public LImage(Bitmap bitmap) {
		setBitmap(bitmap);
		if (!images.contains(this)) {
			images.add(this);
		}
	}

	/**
	 * 重新设置LImage的内部Bitmap。
	 * @param bitmap
	 */
	public void setBitmap(Bitmap bitmap) {
		this.width = bitmap.getWidth();
		this.height = bitmap.getHeight();
		this.bitmap = bitmap;
	}

	/**
	 * 获得对应当前图像资源的Config配置。值得注意的是，Android
	 * 系统并不总能获得正确的图像格式。在Bitmap中，当遇到无法识
	 * 别的格式时它将被返回为NULL，而LImage则强行将它们归结
	 * 为ARGB8888格式（一种非常清晰，也非常消耗资源的格式）。
	 * @return
	 */
	public Config getConfig() {
		Config config = bitmap.getConfig();
		if (config == null) {
			return Config.ARGB_8888;
		}
		return config;
	}

	/**
	 * 依据现有LImage，克隆出另外一个LImage。
	 * @see java.lang.Object#clone()
	 */
	public LImage clone() {
		return new LImage(bitmap);
	}

	/**
	 * 是否拥有透明通道
	 * @return
	 */
	public boolean hasAlpha() {
		if (bitmap == null) {
			return false;
		}
		if (bitmap.getConfig() == Config.RGB_565) {
			return false;
		}
		return bitmap.hasAlpha();
	}

	/**
	 * 返回当前LImage的Graphics 实例。（LGame引擎特有的
	 * LGraphics）
	 * @return
	 */
	public LGraphics getLGraphics() {
		if (g == null || g.isClose()) {
			g = new LGraphics(bitmap);
			isUpdate = true;
		}
		return g;
	}

	/**
	 * 无视是否有打开的Graphics 实例，重新生成一个LGraphics 实例。
	 * @return
	 */
	public LGraphics create() {
		return new LGraphics(bitmap);
	}

	/**
	 * 返回LImage的内部Bitmap。
	 * @return
	 */
	public Bitmap getBitmap() {
		return bitmap;
	}

	/**
	 * 获得当前图像宽度。
	 * @return
	 */
	public int getWidth() {
		return bitmap.getWidth();
	}

	/**
	 * 获得当前图像高度。
	 * @return
	 */
	public int getHeight() {
		return bitmap.getHeight();
	}

	/**
	 * 返回当前图像的像素集合。
	 * @return
	 */
	public int[] getPixels() {
		int pixels[] = new int[width * height];
		bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
		return pixels;
	}

	/**
	 * 将当前图像的像素集合注入指定整型数组并返回。
	 * @param pixels
	 * @return
	 */
	public int[] getPixels(int pixels[]) {
		bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
		return pixels;
	}

	/**
	 * 返回指定范围内的当前图像像素集合。
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public int[] getPixels(int x, int y, int w, int h) {
		int[] pixels = new int[w * h];
		bitmap.getPixels(pixels, 0, w, x, y, w, h);
		return pixels;
	}

	/**
	 * 返回指定范围内的当前图像像素集合。
	 * @param offset
	 * @param stride
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public int[] getPixels(int offset, int stride, int x, int y, int w, int h) {
		int pixels[] = new int[w * h];
		bitmap.getPixels(pixels, offset, stride, x, y, w, h);
		return pixels;
	}

	/**
	 * 返回指定范围内的当前图像像素集合到指定的整型数组中去。
	 * @param pixels
	 * @param offset
	 * @param stride
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 * @return
	 */
	public int[] getPixels(int pixels[], int offset, int stride, int x, int y,
			int width, int height) {
		bitmap.getPixels(pixels, offset, stride, x, y, width, height);
		return pixels;
	}

	/**
	 * 将当前LImage中图像资源设定为指定范围内的像素集合。
	 * @param pixels
	 * @param w
	 * @param h
	 */
	public void setPixels(int[] pixels, int w, int h) {
		bitmap.setPixels(pixels, 0, w, 0, 0, w, h);
	}

	/**
	 * 将当前LImage中图像资源设定为指定范围内的像素集合。
	 * @param pixels
	 * @param offset
	 * @param stride
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 */
	public void setPixels(int[] pixels, int offset, int stride, int x, int y,
			int width, int height) {
		bitmap.setPixels(pixels, offset, stride, x, y, width, height);
	}

	/**
	 * 将当前LImage中图像资源设定为指定范围内的像素集合。
	 * @param pixels
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public int[] setPixels(int[] pixels, int x, int y, int w, int h) {
		bitmap.setPixels(pixels, 0, w, x, y, w, h);
		return pixels;
	}

	/**
	 * 返回指定坐标对应的像素点数据。
	 * @param x
	 * @param y
	 * @return
	 */
	public int getPixel(int x, int y) {
		return bitmap.getPixel(x, y);
	}

	/**
	 * 将当前LImage中图像资源设定为指定范围内的像素集合。(照顾
	 * Java 同名函数习惯)
	 * @param pixels
	 * @param offset
	 * @param stride
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 * @return
	 */
	public int[] getRGB(int pixels[], int offset, int stride, int x, int y,
			int width, int height) {
		bitmap.getPixels(pixels, offset, stride, x, y, width, height);
		return pixels;
	}

	/**
	 * 返回指定坐标对应的像素点数据。(照顾Java 同名函数习惯)
	 * @param x
	 * @param y
	 * @return
	 */
	public int getRGB(int x, int y) {
		return bitmap.getPixel(x, y);
	}

	/**
	 * 设定指定LColor 像素点到当前图形的指定坐标之上。
	 * @param c
	 * @param x
	 * @param y
	 */
	public void setPixel(LColor c, int x, int y) {
		bitmap.setPixel(x, y, c.getRGB());
	}

	/**
	 * 设定指定像素点到当前图形的指定坐标之上。
	 * @param rgb
	 * @param x
	 * @param y
	 */
	public void setPixel(int rgb, int x, int y) {
		bitmap.setPixel(x, y, rgb);
	}

	/**
	 * 设定指定像素点到当前图形的指定坐标之上。(照顾Java 同名函数习惯)
	 * @param rgb
	 * @param x
	 * @param y
	 */
	public void setRGB(int rgb, int x, int y) {
		bitmap.setPixel(x, y, rgb);
	}

	/**
	 * 变更图像Config设置,强制转换LImage为指定图像格式。
	 * 
	 * @param config
	 */
	public void convertConfig(android.graphics.Bitmap.Config config) {
		if (!bitmap.getConfig().equals(config)) {
			boolean flag = bitmap.isMutable();
			Bitmap tmp = bitmap.copy(config, flag);
			bitmap = tmp;
			if (g != null && !g.isClose()) {
				g.dispose();
				g = new LGraphics(bitmap);
			}
		}
	}

	/**
	 * 截小图,从当前图像截取出指定范围内的小图。(有缓存)
	 * 
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public LImage getCacheSubImage(int x, int y, int w, int h) {
		return getCacheSubImage(x, y, w, h, bitmap.getConfig());
	}

	/**
	 * 从当前图像截取出指定范围内的小图，并根据transparency将其
	 * 设定为RGB565（False时）或ARGB4444（True时）格式。（有缓存）
	 * 
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @param transparency
	 * @return
	 */
	public LImage getCacheSubImage(int x, int y, int w, int h, Config config) {
		if (subs == null) {
			subs = new HashMap<Integer, LImage>(10);
		}
		int hashCode = 1;
		hashCode = LSystem.unite(hashCode, x);
		hashCode = LSystem.unite(hashCode, y);
		hashCode = LSystem.unite(hashCode, w);
		hashCode = LSystem.unite(hashCode, h);
		LImage img = (LImage) subs.get(hashCode);
		if (img == null) {
			subs.put(hashCode,
					img = GraphicsUtils.drawClipImage(this, w, h, x, y, config));
		}
		return img;
	}

	/**
	 * 截小图,从当前图像截取出指定范围内的小图。（无缓存）
	 * 
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @param transparency
	 * @return
	 */
	public LImage getSubImage(int x, int y, int w, int h, Config config) {
		return GraphicsUtils.drawClipImage(this, w, h, x, y, config);
	}

	/**
	 * 截小图,从当前图像截取出指定范围内的小图。（无缓存）
	 * 
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @return
	 */
	public LImage getSubImage(int x, int y, int w, int h) {
		return GraphicsUtils
				.drawClipImage(this, w, h, x, y, bitmap.getConfig());
	}

	/**
	 * 扩充图像为指定大小,返回一个指定大小的当前图像资源。
	 * 
	 * @param w
	 * @param h
	 * @return
	 */
	public LImage scaledInstance(int w, int h) {
		int width = getWidth();
		int height = getHeight();
		if (width == w && height == h) {
			return this;
		}
		Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
		return new LImage(resizedBitmap);
	}

	public LColor getColorAt(int x, int y) {
		return new LColor(this.getRGBAt(x, y));
	}

	public int getRGBAt(int x, int y) {
		if (x >= this.getWidth()) {
			throw new IndexOutOfBoundsException("X is out of bounds: " + x
					+ "," + this.getWidth());
		} else if (y >= this.getHeight()) {
			throw new IndexOutOfBoundsException("Y is out of bounds: " + y
					+ "," + this.getHeight());
		} else if (x < 0) {
			throw new IndexOutOfBoundsException("X is out of bounds: " + x);
		} else if (y < 0) {
			throw new IndexOutOfBoundsException("Y is out of bounds: " + y);
		} else {
			return bitmap.getPixel(x, y);
		}
	}

	/**
	 * 返回LImage的hash序列。
	 * 获得当前图像的哈希序列。（此为依据图像像素点，采取快速截
	 * 取法生成的hash，有可能在近似图中存在误差）
	 * 
	 */
	public int hashCode() {
		return GraphicsUtils.hashBitmap(bitmap);
	}

	public Format getFormat() {
		return format;
	}

	public void setFormat(Format format) {
		this.format = format;
		this.isUpdate = true;
	}

	public LTexture getTexture() {
		if (texture == null || isUpdate) {
			setAutoDispose(false);
			LTexture tmp = texture;
			texture = new LTexture(GLLoader.getTextureData(this), format);
			if (tmp != null) {
				tmp.dispose();
				tmp = null;
			}
			isUpdate = false;
		}
		return texture;
	}

	/**
	 * 判定当前LImage是否已被关闭
	 * 
	 * @return
	 */
	public boolean isClose() {
		return isClose || bitmap == null
				|| (bitmap != null ? bitmap.isRecycled() : false);
	}

	public boolean isAutoDispose() {
		return isAutoDispose && !isClose();
	}

	public void setAutoDispose(boolean dispose) {
		this.isAutoDispose = dispose;
	}

	public LPixmapData newPixmap() {
		return new LPixmapData(this);
	}

	public String getPath() {
		return fileName;
	}

	/**
	 * 释放当前LImage资源。（主要为recycle内部的Bitmap）
	 * @see org.loon.framework.android.game.core.LRelease#dispose()
	 */
	public void dispose() {
		dispose(true);
	}

	private void dispose(boolean remove) {
		isClose = true;
		subs = null;
		if (bitmap != null) {
			bitmap.recycle();
			bitmap = null;
		}
		if (texture != null && isAutoDispose) {
			texture.dispose();
			texture = null;
		}
		if (remove) {
			images.remove(this);
		}
	}

	public static void disposeAll() {
		if (images.size() > 0) {
			for (LImage img : images) {
				if (img != null) {
					img.dispose(false);
					img = null;
				}
			}
			images.clear();
		}
	}
}
